package com.springbootblog.controller.fontend;

import com.alibaba.fastjson.JSONObject;
import com.springbootblog.dao.ChatRecordDao;
import com.springbootblog.dao.UserDao;
import com.springbootblog.pojo.ChatRecord;
import com.springbootblog.pojo.User;
import com.springbootblog.utils.Function;
import com.springbootblog.utils.RedisUtil;
import com.springbootblog.utils.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.CrossOrigin;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * webscoket部分接口实现
 * @className AboutController
 * @author camellia
 * @email 805795955@qq.com
 * @date 2024/01/01
 * @version 1.0
 */
@Component                              // 组件
@Slf4j                                  // 日志依赖
@Service
@ServerEndpoint("/api/websocket/{sid}") // 配置webscoket访问链接
public class WebSocketServer
{
    /**
     * 注入redis工具类
     */
    // @Autowired
    // private RedisUtil redisUtil;
    private RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);

    /**
     * 自动装载（dao接口注入）
     */
    // @Autowired
    // private UserDao userDao;
    private UserDao userDao = SpringUtil.getBean(UserDao.class);

    /**
     * 自动装载（dao接口注入）
     */
    // @Autowired
    // private ChatRecordDao chatDao;
    private ChatRecordDao chatDao = SpringUtil.getBean(ChatRecordDao.class);

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    public static int onlineCount = 0;
    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    //接收sid
    public String sid = "";

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid)
    {
        this.session = session;
        //加入set中
        webSocketSet.add(this);
        this.sid = sid;
        //在线数加1
        addOnlineCount();
        try
        {
            Map<String,Object> result = new HashMap<>(2);
            result.put("code", 1);
            result.put("msg", "webscoket链接成功");
            sendMessage(JSONObject.toJSONString(result));
            System.out.println("有新窗口开始监听:" + sid + ",当前在线人数为:" + getOnlineCount());
        }
        catch (IOException | EncodeException e)
        {
            System.out.println("websocket IO Exception");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose()
    {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        //断开连接情况下，更新主板占用情况为释放
        System.out.println("释放的sid为："+sid);
        //这里写你 释放的时候，要处理的业务
        System.out.println("有一连接关闭！当前在线人数为" + getOnlineCount());

    }

    /**
     * 收到客户端消息后调用的方法
     * @ Param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) throws JSONException, IOException, EncodeException {
        this.session = session;
        System.out.println("收到来自窗口" + sid + "的信息:" + message);
        // 获取key对应的值
        String type = Function.stringToJson(message,"type");
        String from_user_id = Function.stringToJson(message,"from_user_id");
        String to_user_id = Function.stringToJson(message,"to_user_id");
        String msg = Function.stringToJson(message,"msg");
        String fd = Function.stringToJson(message,"fd");

        // 用户聊天
        if ((from_user_id.equals("") || to_user_id.equals("")) && type.equals("userchat"))
        {
            Map<String,Object> result = new HashMap<>(2);
            result.put("code", -1);
            result.put("msg", "参数错误！请重新登录~");
            sendMessage(JSONObject.toJSONString(result));
        }

        if(type.equals("ping"))
        {// 心跳
            // 在线用户列表
            Map<String,Object> array = getWebscoketUserList(from_user_id, session.getId());
            ArrayList<Map<String,Object>> userList = (ArrayList<Map<String, Object>>) array.get("userList");
            Integer totalUnReadNumber = (Integer) array.get("totalUnReadNumber");

            // System.out.println("totalUnReadNumber:"+totalUnReadNumber);
            // System.out.println("userList:"+userList);

            Map<String,Object> result = new HashMap<>(4);
            result.put("code", 1);
            result.put("type", type);
            result.put("list", userList);
            result.put("totalUnReadNumber", totalUnReadNumber);
            // System.out.println(JSONObject.toJSONString(result));
            sendMessage(JSONObject.toJSONString(result));
        }
        else if(type.equals("userchat"))
        {// 用户一对一聊天
            // System.out.println("type:"+type);
            // System.out.println("from_user_id:"+from_user_id);
            // System.out.println("to_user_id:"+to_user_id);
            // System.out.println("msg:"+msg);
            // System.out.println("fd:"+fd);
            // 保存聊天记录
            saveChatRecord(from_user_id,to_user_id,msg);
            // Map<String,Object> param = new HashMap<>(1);
            // param.put("answer", msg);
            Map<String,Object> result = new HashMap<>(2);
            result.put("response", "");
            result.put("type", type);
            sendMessage(JSONObject.toJSONString(result));
        }
        session.getId();
        //群发消息
        /*for (WebSocketServer item : webSocketSet)
        {
            try
            {
                item.sendMessage(message);
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }//*/
    }

    public Map<String,Object> saveChatRecord(String from_user_id, String to_user_id,String msg)
    {
        ChatRecord chatRecord = new ChatRecord();
        chatRecord.setFrom_user_id(Integer.valueOf(from_user_id));
        chatRecord.setTo_user_id(Integer.valueOf(to_user_id));
        chatRecord.setMsg(msg);
        Integer res = chatDao.addChatRecord(chatRecord);
        Map<String,Object> result = new HashMap<>(2);
        result.put("code", 1);
        result.put("id", res);
        return result;
    }

    /**
     * 获取webscoket链接的用户列表
     * @param user_id
     * @param fd
     * @return
     */
    public Map<String,Object> getWebscoketUserList(String user_id,String fd)
    {
        // 存redis ，10分钟过期，防止用户退出之后redis中存储的数据一直存在。
        // 存储格式：webscoket-用户id : webscoket连接主键
        // 组装redis-key
        String redisKey = "webscoket-" + user_id;
        String webscoketFD = "";
        // 首先获取当前key值(webscoket 连接主键)
        // System.out.println(redisKey);
        // System.out.println("redisUtil:"+redisUtil.get(redisKey));
        // System.out.println("fd:"+fd);
        if(redisUtil.get(redisKey) != null )
        {
            webscoketFD = redisUtil.get(redisKey).toString();
        }
        // System.out.println("webscoketFD:"+webscoketFD);
        // 判断当前主键是否redis中记录的主键一致。(就是判断这个玩意redis中有没有)
        if(!webscoketFD.equals(fd))
        {
            // 设置一个有过期时间的key
            redisUtil.set(redisKey, fd,60*60*2);
            // System.out.println("存储之后的redis:"+redisUtil.get(redisKey));
        }
        // 获取指定前缀的Key(返回一个数组)
        List<String> keyList = redisUtil.findKeysByPattern("webscoket-*");
        // System.out.println(keyList);
        Integer totalUnReadNumber = 0;
        ArrayList<Map<String,Object>> array = new ArrayList<>();
        for (int i = 0; i < keyList.size(); i++)
        {
            Object key = keyList.get(i);
            // System.out.println(key);
            String[] arr = key.toString().split("-");
            if(arr.length < 2)
            {
                break;
            }
            String to_user_id = arr[1];
            String value = redisUtil.get(key.toString()).toString();
            // System.out.println(to_user_id);
            // System.out.println(value);
            Map<String,Object> temp = new HashMap<>(5);
            temp.put("fd", value);
            // 查未读消息数量
            Integer unReadNumber = chatDao.getUnReadNumber(user_id,to_user_id);
            totalUnReadNumber += unReadNumber;
            // 查用户信息
            User userinfo = userDao.getUserInfoById(to_user_id);
            if(userinfo == null)
            {
                redisUtil.del(key.toString());
                continue;
            }
            if(!userinfo.getFigureurl_wx().equals(""))
            {
                temp.put("userlogo", userinfo.getFigureurl_wx());
            }
            else
            {
                temp.put("userlogo", userinfo.getFigureurl_qq());
            }
            temp.put("username", userinfo.getNickname());
            temp.put("id", userinfo.getId());
            array.add(temp);
        }
        Map<String,Object> result = new HashMap<>(2);
        result.put("userList", array);
        result.put("totalUnReadNumber", totalUnReadNumber);
        return result;
    }

    /**
     * @ Param session
     * @ Param error
     */
    @OnError
    public void onError(Session session, Throwable error)
    {
        // log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException, EncodeException
    {
        this.session.getBasicRemote().sendText(message);
        // this.session.getBasicRemote().sendObject(message);
    }

    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException
    {
        // System.out.println("推送消息到窗口" + sid + "，推送内容:" + message);

        for (WebSocketServer item : webSocketSet)
        {
            try
            {
                //这里可以设定只推送给这个sid的，为null则全部推送
                if (sid == null)
                {
                    // item.sendMessage(message);
                }
                else if (item.sid.equals(sid))
                {
                    item.sendMessage(message);
                }
            }
            catch (IOException e)
            {
                continue;
            } catch (EncodeException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static synchronized int getOnlineCount()
    {
        return onlineCount;
    }

    public static synchronized void addOnlineCount()
    {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount()
    {
        WebSocketServer.onlineCount--;
    }
}

